﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MeshEffectSystem : MonoBehaviour
{
    public const float TexOffsetRange = 4.0f;
    public const float TexScaleRange = 10.0f;
    public const float mColorRange = 10.0f;
    public static int offsetMul = Shader.PropertyToID("offsetMul");
    public static int offsetAdd = Shader.PropertyToID("offsetAdd");

    //float 
    float _time;
    float interval;
    Vector2 mainTiling;

    [Header("播放时间")]
    public float lifeTime = 1;

    [Header("是否循环")]
    public bool looping = true;

    [Header("颜色")]
    public Color color;

    [Header("是否播放序列帧")]
    public bool isFrameAnimation = false;

    [Header("序列帧")]
    [Tooltip("格子列数")]
    public int gridX = 4;
    [Tooltip("格子行数")]
    public int gridY = 4;

    [Tooltip("起始序列")]
    public int startFrame = 0;
    [Tooltip("结束序列")]
    public int endFrame = 3;

    [Header("缩放")]
    public bool isScale = false;
    public AnimationCurve scaleCurveX;
    public AnimationCurve scaleCurveY;
    public AnimationCurve scaleCurveZ;

    [Header("Gradient")]
    public bool isGradientColor = true;
    public Gradient gradientColor;

    [Header("是否Billboard")]
    public bool isBillboard = true;
    [Header("是否Y轴Billboard")]
    public bool isYBoardboard = true;

    [System.NonSerialized]
    List<Vector4> uvBuffer = null;

    [System.NonSerialized]
    int[] sheetData = null;

    public Mesh sharedMesh;
    public Material sharedMaterial;
    public MeshFilter[] meshFilters = null;
    public MeshRenderer[] meshRenders = null;
    public Transform[] meshTransforms = null;
    public Vector4 MainBlock = new Vector4(1, 1, 0, 0);
    private Camera mainCamera;
    void OnEnable()
    {
        if (!Application.isPlaying)
            return;

        if (sharedMesh == null || meshFilters == null)
            return;
        
        mainCamera = Camera.main;
        _time = 0;
        if (isFrameAnimation && (gridX * gridY) > 1)
        {
            startFrame = Mathf.Max(startFrame, 0);
            endFrame = Mathf.Min(endFrame, (gridX * gridY) - 1);
            mainTiling = new Vector2(1.0f / gridX, 1.0f / gridY);
        }
        else
        {
            startFrame = Mathf.Max(startFrame, 0);
            endFrame = Mathf.Max(endFrame, 1);
            mainTiling = Vector2.one;
        }
        interval = lifeTime / (endFrame - startFrame);

        sheetData = new int[meshFilters.Length];
        for (int mf = 0; mf < sheetData.Length; mf++)
        {
            sheetData[mf] = Random.Range(startFrame, endFrame);
        }

        //if (isBillboard || isYBoardboard)
        //{
        //    meshTransforms = new Transform[meshFilters.Length];
        //    for (int mf = 1; mf < meshFilters.Length; mf++)
        //    {
        //        meshTransforms[mf] = meshFilters[mf].transform;
        //    }
        //}

        uvBuffer = EffectMeshUtility.GetList(sharedMesh.vertexCount);

        float a1 = EffectMeshUtility.V2Tofloat(MainBlock.x, MainBlock.y);
        float a2 = EffectMeshUtility.V2Tofloat(MainBlock.z, MainBlock.w);
        Vector2 _pack0 = new Vector2(a1, a2);

        sharedMesh.GetUVs(0, uvBuffer);
        for (int i = 0; i < uvBuffer.Count; i++)
        {
            Vector4 tex = uvBuffer[i];
            tex.z = _pack0.x;
            tex.w = _pack0.y;
            uvBuffer[i] = tex;
        }

#if true
        meshFilters[0].sharedMesh = Mesh.Instantiate(sharedMesh);
        meshFilters[0].sharedMesh.SetUVs(0, uvBuffer);
        for (int mf = 1; mf < meshFilters.Length; mf++)
        {
            MeshFilter filter = meshFilters[mf];
            filter.sharedMesh = Mesh.Instantiate(meshFilters[0].sharedMesh);
        }
#else
        for (int mr = 0; mr < meshRenders.Length; mr++)
        {
            MeshRenderer renderer = meshRenders[mr];
            Mesh sharedMesh = new Mesh();
            sharedMesh.SetUVs(0, uvBuffer);
            renderer.additionalVertexStreams = sharedMesh;
        }
#endif
        if (sharedMaterial != null)
        {
            sharedMaterial.SetFloat(offsetMul, 2.0f * TexOffsetRange);
            sharedMaterial.SetFloat(offsetAdd, -TexOffsetRange);
        }
    }

    void OnDestroy()
    {
        for (int mf = 0; meshFilters != null && mf < meshFilters.Length; mf++)
        {
            MeshFilter filter = meshFilters[mf];
            Object.DestroyImmediate(filter.sharedMesh);
        }

        uvBuffer = null;
        sheetData = null;
    }

    void Update()
    {
        if (isBillboard)
        {
            if (isYBoardboard)
            {
                for (int mf = 0; mf < meshTransforms.Length; mf++)
                {
                    Vector3 v = mainCamera.transform.position - meshTransforms[mf].position;
                    v.x = v.z = 0.0f;
                    meshTransforms[mf].LookAt(mainCamera.transform.position - v);
                }
            }
            else
            {
                for (int mf = 0; mf < meshTransforms.Length; mf++)
                    meshTransforms[mf].LookAt(mainCamera.transform);
            }
        }

        _time += Time.deltaTime;
        if (_time < interval)
            return;

        _time = 0;

        if (uvBuffer == null || sheetData == null)
            return;

        bool bFrameAim = isFrameAnimation && (gridX * gridY) > 1;
        for (int mf = 0; mf < meshFilters.Length; mf++)
        {
            int frame = sheetData[mf] + 1;
            if (!looping && frame > endFrame)
            {
                meshRenders[mf].enabled = false;
                continue;
            }

            sheetData[mf] = Mathf.Clamp(frame % endFrame, startFrame, endFrame);

            if (!meshRenders[mf].enabled)
                continue;

            Vector4 vcolor = color;
            if (isGradientColor)
            {
                float _gradientTime = (float)sheetData[mf] * interval;
                vcolor = gradientColor.Evaluate(_gradientTime) * vcolor;
            }

            int x = !bFrameAim ? 1 : frame % gridX;
            int y = !bFrameAim ? 1 : gridY - Mathf.FloorToInt(frame / gridX) - 1;

            Vector2 mainOffset = new Vector2(x * mainTiling.x, y * mainTiling.y);
            float c1 = EffectMeshUtility.PackM(vcolor.x, vcolor.y, mColorRange);
            float c2 = EffectMeshUtility.PackM(vcolor.z, vcolor.w, mColorRange);
            float d1 = EffectMeshUtility.PackScale(mainTiling, TexScaleRange);
            float d2 = EffectMeshUtility.PackOffset(mainOffset, TexOffsetRange);
            Vector4 pack1 = new Vector4(c1, c2, d1, d2);

            for (int i = 0; i < uvBuffer.Count; i++)
            {
                uvBuffer[i] = pack1;
            }

#if true
            meshFilters[mf].sharedMesh.SetUVs(1, uvBuffer);
#else
            meshRenders[mf].additionalVertexStreams.SetUVs(1, uvBuffer);
#endif
        }
    }
}
